﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Dycox.Workflows.Manipulation
{
    internal abstract class TransitionContext
    {
        private void Initialize()
        {
            _transition = _executionContext.Workflow.Transitions[_executionContext.Stage, _upcomingStage];

            if (_transition != null)
            {
                Debug.Assert(_transition.Type == TransitionType);

                _wait = _transition.Operations.OfType<ThreadLockWaitOperation>().SingleOrDefault();
                _release = _transition.Operations.OfType<ThreadLockReleaseOperation>().SingleOrDefault();

                Debug.Assert(_wait == null || _release == null || _wait.LockName == _release.LockName);

                ThreadLockOperation tlo = (ThreadLockOperation)_wait ?? (ThreadLockOperation)_release;

                if (tlo != null)
                    _exitSyncLock = tlo.GetLock(_executionContext.Process);
            }
        }


        protected TransitionContext(WorkflowForwardingContext context, StepContext upcomingStep, Stage upcomingStage)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            
            var incomingStage = context.Stage;

            Debug.Assert(context != null || upcomingStep != null);
            Debug.Assert(context == null || upcomingStep == null || context.Process == upcomingStep.Process);
            Debug.Assert(incomingStage != null && upcomingStage != null);
            Debug.Assert(incomingStage.Workflow == upcomingStage.Workflow);

            //_incomingStep = context;
            //_incomingStage = incomingStage;
            _upcomingStep = upcomingStep;
            _upcomingStage = upcomingStage;
            _executionContext = context;

            Initialize();
        }

        public abstract TransitionTypes TransitionType
        {
            get;
        }

        private WorkflowForwardingContext _executionContext;

        public WorkflowForwardingContext ExecutionContext
        {
            get { return _executionContext; }
        }


        private StepContext _upcomingStep;

        public StepContext UpcomingStep
        {
            get { return _upcomingStep; }
        }

        //private Stage _incomingStage;

        public Stage IncomingStage
        {
            get { return _executionContext.Stage; }
        }

        private Stage _upcomingStage;

        public Stage UpcomingStage
        {
            get { return _upcomingStage; }
        }

        //private StepContext _incomingStep;

        public StepContext IncomingStep
        {
            get { return _executionContext; }
        }

        private Transition _transition;

        public Transition Transition
        {
            get { return _transition; }
        }

        public bool Exists
        {
            get
            {
                return _transition != null;
            }
        }

        private ThreadLockReleaseOperation _release;

        public ThreadLockReleaseOperation Release
        {
            get { return _release; }
        }

        private ThreadLockWaitOperation _wait;

        public ThreadLockWaitOperation Wait
        {
            get { return _wait; }
        }

        public bool ShouldWait
        {
            get
            {
                if (_wait != null)
                    return _exitSyncLock.ShouldWait(_executionContext.Process);
                else
                    return false;
            }
        }

        ThreadLock _exitSyncLock;
        public ThreadLock ExitSyncLock
        {
            get
            {
                return _exitSyncLock;
            }
        }

        protected virtual TransitionOperationArguments CreateOperationArgs()
        {
            return new TransitionOperationArguments(_executionContext, _upcomingStep);
        }

        public void PerformOperations()
        {
            if (Exists)
            {
                TransitionOperationArguments args = CreateOperationArgs();
                
                ConditionTestArguments cta = new ConditionTestArguments(
                    _executionContext.Step, _executionContext.UserState, _executionContext.Stage, _executionContext.AppObject);

                foreach (var op in _transition.Operations)
                {
                    if (op.ShouldPerform(cta))
                        op.Perform(args);
                }
            }
            
        }
    }

    internal class ExitStageTransitionContext : TransitionContext
    {
        public ExitStageTransitionContext(WorkflowForwardingContext context)
            : base(context, null, context.Workflow.AnyStage)
        {
        }

        public override TransitionTypes TransitionType
        {
            get { return TransitionTypes.ExitStage; }
        }

    }

    internal class NormalStageTransitionContext : TransitionContext
    {
        public NormalStageTransitionContext(WorkflowForwardingContext context, StepContext upcomingStep)
            : base(context, upcomingStep, upcomingStep.Stage)
        {

        }

        public override TransitionTypes TransitionType
        {
            get { return TransitionTypes.Normal; }
        }

    }

    internal class NoMoreStepTransitionContext : TransitionContext
    {
        public NoMoreStepTransitionContext(WorkflowForwardingContext context)
            : base(context, null, context.Workflow.NoMoreStep)
        {

        }

        public override TransitionTypes TransitionType
        {
            get { return TransitionTypes.NoMoreStep; }
        }

    }
}
